﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace TerrariaSvcCtrl {
    public class Worker {
        MOTDWorker motdWorker;

        public bool SvcRunning { get; set; }
        public bool RequestSvcStop { get; set; }
        Process svrProcess;
        WorkerArgs inputArgs;
        System.Timers.Timer autoSaveTimer;
        ProcessStartInfo psi;

        string rconPassword = "alex";
        string rconPhrase;

        public event ServerMessage onServerMessage;
        public delegate void ServerMessage(string message);

        //duck has joined
        public event PlayerJoin onPlayerJoin;
        public delegate void PlayerJoin(string username);

        //duck has left
        public event PlayerLeft onPlayerLeft;
        public delegate void PlayerLeft(string username);

        // <duck> sample chat from duck
        public event PlayerChat onPlayerChat;
        public delegate void PlayerChat(string username, string message);



        /// <summary>
        /// Switch service control on disposal
        /// </summary>
        ~Worker() {
            this.SvcRunning = false; //make sure it dies
        }

        public void StartService(object state) {
            //preload cleanup?
            this.rconPhrase = string.Format("rcon {0}", this.rconPassword);

            //start the stuff!
            doWork((TerrariaSvcCtrl.WorkerArgs)state);
        }

        void doWork(WorkerArgs args) {
            inputArgs = args;
            inputArgs.EnableLogging = true;
            inputArgs.AdminIP = "10.0.0.2";

            //flip the bits
            this.RequestSvcStop = false;
            this.SvcRunning = true;

            //build process info (passed on build)
            //start process
            startProcess(buildProcess());
            //save once in a while

            //broadcast random messages
            motdWorker = new MOTDWorker();
            motdWorker.onMOTDTick += new MOTDWorker.MOTDMessage(motdWorker_onMOTDTick);
            ThreadPool.QueueUserWorkItem(new WaitCallback(motdWorker.DoWork), 10);
        }

        void motdWorker_onMOTDTick(string message) {
            //throw new NotImplementedException();
            this.SendChat(message);
        }

        ProcessStartInfo buildProcess() {
            //base install directory, dynamic/args later
            //C:\Program Files (x86)\Steam\SteamApps\common\Terraria
            //C:\Program Files\Steam\SteamApps\common\Terraria

            psi = new ProcessStartInfo();
            psi.Arguments = string.Format("-config {0}", "serverconfig.txt"); //static for now.. dynamic later
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            psi.WindowStyle = ProcessWindowStyle.Hidden;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardError = true;
            psi.FileName = "TerrariaServer.exe";

#warning "TerrariaServer.exe is hard coded for location but it might be okay..."
            if (File.Exists(@"C:\Program Files (x86)\Steam\SteamApps\common\Terraria\TerrariaServer.exe")) {
                psi.WorkingDirectory = @"C:\Program Files (x86)\Steam\SteamApps\common\Terraria\";
            } else if (File.Exists(@"C:\Program Files\Steam\SteamApps\common\Terraria\TerrariaServer.exe")) {
                psi.WorkingDirectory = @"C:\Program Files\Steam\SteamApps\common\Terraria\";
            } else {
                //not ready for this yet!
                this.RequestSvcStop = true;
            }

            return psi;
        }

        public void SaveWorld() {
            SendCommand("save");
        }

        public void SendChat(string message) {
            try {
                svrProcess.StandardInput.WriteLine(string.Format("say {0}", message));
            } catch (Exception err) {
                Console.Error.WriteLine(err);
            }
        }


        public void SendCommand(string cmd) {
            try {
                svrProcess.StandardInput.WriteLine(cmd);
            } catch (Exception err) {
                Console.Error.WriteLine(err);
            }
        }

        void startProcess(ProcessStartInfo psi) {
            svrProcess = new Process();
            svrProcess.StartInfo = psi;

            svrProcess.OutputDataReceived += new DataReceivedEventHandler(svrProcess_OutputDataReceived);
            svrProcess.ErrorDataReceived += new DataReceivedEventHandler(svrProcess_ErrorDataReceived);

            //ugly but whatever
            autoSaveTimer = new System.Timers.Timer();
            autoSaveTimer.Elapsed += new System.Timers.ElapsedEventHandler(autoSaveTimer_Elapsed);
            autoSaveTimer.Interval = ((1000 * 60) * 10); //save every 10 minutes
            autoSaveTimer.Enabled = true;

            svrProcess.Start();
            svrProcess.BeginOutputReadLine();

            //svrProcess.WaitForExit();
        }

        void autoSaveTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
            //throw new NotImplementedException();
            //SendCommand(string.Format("say {0} - Saving the world.... Lag monsters are attacking!", DateTime.Now.ToLongTimeString()));
            SaveWorld();
            SendCommand(string.Format("say Autosave complete.", DateTime.Now.ToLongTimeString()));
        }
        
        void sendToSplunk(Object state) {
            string message = (string)state;
            try {
                SplunkTCP.TCPSender sender = new SplunkTCP.TCPSender();
                sender.Send("192.168.1.53", 16948, message);
            } catch (Exception err) { }
        }


        //Event stuff
        void svrProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) {
            //throw new NotImplementedException();
        }

        void svrProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) {
            //output to text log?
            if (this.inputArgs.EnableLogging) {
                if (!String.IsNullOrEmpty(e.Data)) {
                    string message = string.Format("{0} -- {1}", DateTime.Now, e.Data);

                    //send to splunk
                    ThreadPool.QueueUserWorkItem(new WaitCallback(this.sendToSplunk), message);
                    
                    using (StreamWriter sw = new StreamWriter(@"c:\temp\TerrariaSvcCtrl.log", true)) {
                        sw.WriteLine(message);
                    }
                }
            }

            //stroll thru the data and see what type it is, maybe there's an event!
            if (e.Data.StartsWith(": <")) {
                //it's a chat message
                // <duck> hello everyone
            } else if (e.Data.StartsWith(string.Format(":thisdoesntworkright {0}", inputArgs.AdminIP))) {
                //trusted admins
                //got to make this cleaner too.. haha alot TODO
            } else {
                //it's a system message
                //duck has joined
                if (e.Data.EndsWith("has joined.")) {
                    if (onPlayerJoin != null) {
                        string playerName = e.Data.Substring(0, e.Data.IndexOf("has joined"));
                        onPlayerJoin(playerName);
                    }
                } else if (e.Data.EndsWith("has left.")) {
                    if (onPlayerLeft != null) {
                        string playerName = e.Data.Substring(0, e.Data.IndexOf("has left"));
                        onPlayerLeft(playerName);
                    }
                } else {
                    //some other system message
                    if (onServerMessage != null) {
                        onServerMessage(e.Data);
                    }
                }
            }

            //the above is messy.. lets clean it up a bit but allow rcon
            if (e.Data.Contains(rconPhrase)) {
                string cmd = e.Data.Substring(e.Data.IndexOf(this.rconPhrase) + this.rconPhrase.Length + 1); //include/assume trailing space
                this.SendCommand(cmd);
            }


        }


    }
}
